package kibahed.university.effizientealgorithmen.fileinteractions;

import java.io.*;
import java.util.*;


public class TSPLIB_Parser
{

    final static int EOF = 0;
    final static int NAME = 1;
    final static int TYPE = 2;
    final static int DIM = 3;
    final static int WEIGHTTYPE = 4;
    final static int NODETYPE = 5;
    final static int NODESECTION = 6;

    protected BufferedReader quelle;

    protected String instanzName;
    protected static double[][] vertex;
    protected int weightType;

    TSPLIB_Parser(String name) throws java.io.IOException, TSPParseException 
    {

        
    if (!name.endsWith(".tsp"))
        throw new TSPParseException("Der Dateiname muss mit \".tsp\" enden!");

    File f = new File(name);
    
    if (!f.exists())
        throw new TSPParseException("\"" + name + "\" existiert nicht!");

    if (!f.isFile())
        throw new TSPParseException("\"" + name + "\" ist keine Datei!");

    quelle =  new BufferedReader(new InputStreamReader(new FileInputStream(f)));

    instanzName = name;
    vertex = null;
    weightType = -1;

    parse();
    }
    
    public TSPLIB_Parser(File file) throws java.io.IOException, TSPParseException
    {
    	
    	quelle =  new BufferedReader(new InputStreamReader(new FileInputStream(file)));
    	instanzName = file.getName();
        vertex = null;
        weightType = -1;

        parse();
    }

    public String getName() 
    {
    	return instanzName;
    }

    public  double[][] getVertices() 
    {
    	return vertex;
    }
    
    public  void printData()
    {
        String string = "";
        for(int i=0; i<vertex.length;i++)
        {
            string+=i+": ";
            for(int y=0; y<vertex[0].length;y++)
            {
                string+=vertex[i][y]+";";
            }
            string+="\n";
        }
        System.out.println(string);
    }
    /**
     * returns true if a value with NAchkommastellen has been found, otherwise false is returned
     */
    
    public boolean hasNachkomma()
    {
        long nurInt;
        double kommas;
        boolean hasNachkomma=false;
        for(int i=0; i<vertex.length;i++)
        {
            for(int y=0; y<vertex[0].length;y++)
            {
               nurInt = (long)vertex[i][y];
               kommas = vertex[i][y] - nurInt;
               if(kommas!=0) 
               {
                   hasNachkomma=true;
                   System.out.println("Value has Nachkomma:"+i+","+y);
                   return hasNachkomma;
               }
            }
        }
        return hasNachkomma;
    }
    
    /**
     * Converts the double array storing into an int array storing
     */
    public int[][] toIntArray()
    {
        int[][] returnfield = new int[vertex.length][vertex[0].length];
       for(int x=0; x<vertex.length;x++)
        {
            for(int y=0; y<vertex[0].length;y++)
            {
               returnfield[x][y] = (int)vertex[x][y];
            }
        }
        return returnfield;
    }

    
    /**
     * converts the Array into a Matrix form of the graph
     */
    public  double[][] toDoubleMatrix()
    {
        double[][] returnfield = new double[vertex.length][vertex.length];
        for(int from =0;from<returnfield.length;from++)
            {
                for(int to=0;to<returnfield.length;to++)
                {
                    if(from==to)
                    {
                        returnfield[from][to]=Double.MAX_VALUE;
                    }
                    else
                    {
                        // xd = x(from)-x(to)
                        // yd = y(from)-y(to)
                        // distance=sqrt(xd*xd+yd*yd)
                        //(xd)*(xd)
                        returnfield[from][to]+=(vertex[from][0]-vertex[to][0])*(vertex[from][0]-vertex[to][0]);
                        //(yd)*(yd)
                        returnfield[from][to]+=(vertex[from][1]-vertex[to][1])*(vertex[from][1]-vertex[to][1]);
                        //Wurzel Ziehen
                        returnfield[from][to]=Math.sqrt(returnfield[from][to]);
                    }
                }
            }
     return returnfield;
    }

    /**
     * converts the Array into an Int Matrix form of the graph
     */
    public int[][] toIntMatrix()
    {
        int[][] returnfield = new int[vertex.length][vertex.length];
        for(int from =0;from<returnfield.length;from++)
            {
                for(int to=0;to<returnfield.length;to++)
                {
                    if(from==to)
                    {
                        returnfield[from][to]=Integer.MAX_VALUE;
                    }
                    else
                    {
                        // xd = x(from)-x(to)
                        // yd = y(from)-y(to)
                        // distance=sqrt(xd*xd+yd*yd)
                        //(xd)*(xd)
                        returnfield[from][to]+=(vertex[from][0]-vertex[to][0])*(vertex[from][0]-vertex[to][0]);
                        //(yd)*(yd)
                        returnfield[from][to]+=(vertex[from][1]-vertex[to][1])*(vertex[from][1]-vertex[to][1]);
                        //Wurzel Ziehen
                        returnfield[from][to]=(int)Math.round(Math.sqrt(returnfield[from][to]));
                    }
                }
            }
     return returnfield;
    }
    
    public int getWeightType() {
    return weightType;
    }
    protected void parse() throws java.io.IOException, TSPParseException {
    
    int section = -1;
    String str = null;
    boolean typeIsChecked = false;
    boolean weightTypeIsChecked = false;    
    do {
        str = quelle.readLine();
        section = getSection(str);
        
        if (section != -1) {        
        switch (section) {
        case NAME:
            readName(str);
            break;
        case TYPE:
            checkType(str);
            typeIsChecked = true;
            break;
        case DIM:
            initVertexArray(str);
            break;
        case WEIGHTTYPE:
            readWeightType(str);
            weightTypeIsChecked = true; 
            break;
        case NODETYPE:
            checkNodeType(str);
            break;
        case NODESECTION:
            readVertices();         
            break;
        }
        }
        
    } while (!((section == EOF) || (str == null)));
    
    if (!(typeIsChecked && weightTypeIsChecked))
        throw new TSPParseException("Diese Datei ist von unbekanntem (Kanten-)Typ!");      
    }

    protected int getSection(String str) throws IOException {

    if (str == null)
        return EOF;

    StringTokenizer tokenizer = new StringTokenizer(str, " :");
    
    if (!tokenizer.hasMoreTokens())
        return -1;
    
    String firstToken = tokenizer.nextToken();

    //  System.out.println(firstToken);
    
    if (firstToken.equalsIgnoreCase("eof"))
        return EOF;
    
    if (firstToken.equalsIgnoreCase("name"))
        return NAME;
    
    if (firstToken.equalsIgnoreCase("type"))
        return TYPE;
    
    if (firstToken.equalsIgnoreCase("dimension"))
        return DIM;
    
    if (firstToken.equalsIgnoreCase("edge_weight_type"))
        return WEIGHTTYPE;
    
    if (firstToken.equalsIgnoreCase("node_coord_type"))
        return NODETYPE;
    
    if (firstToken.equalsIgnoreCase("node_coord_section"))
        return NODESECTION;
    
    return -1;
    }
 

    protected void readName(String str) {
    StringTokenizer tokenizer = new StringTokenizer(str, ":");
    tokenizer.nextToken();
    
    if (tokenizer.hasMoreTokens()) {
        instanzName = tokenizer.nextToken();
        instanzName = instanzName.trim();
    }
    }

    protected void checkType(String str) throws TSPParseException {
    StringTokenizer tokenizer = new StringTokenizer(str, ": ");
    tokenizer.nextToken();

    if (tokenizer.hasMoreTokens()) {
        String type = tokenizer.nextToken();

        if (!type.equalsIgnoreCase("tsp"))
        throw new TSPParseException("Diese Datei ist nicht vom Typ \"TSP\"!");
    }
    else
        throw new TSPParseException("Diese Datei ist von unbekanntem Typ!");        
    }

    
    protected void initVertexArray(String str) throws TSPParseException {
    StringTokenizer tokenizer = new StringTokenizer(str, ":");
    tokenizer.nextToken();
    
    if (tokenizer.hasMoreTokens()) {
        String dimension = tokenizer.nextToken();
        dimension = dimension.trim();
        
        int dim = 0;
        try {
        dim = Integer.valueOf(dimension).intValue();
        }
        catch(NumberFormatException e) {
        throw new TSPParseException("Die Dimension konnte nicht eingelesen werden!: " + e.getMessage());
        }       
        
        vertex = new double[dim][2];
    }
    else
        throw new TSPParseException("In dieser Datei ist keine Dimension angegeben!");
    }
    
    protected void readWeightType(String str) throws TSPParseException {
    StringTokenizer tokenizer = new StringTokenizer(str, ":");
    tokenizer.nextToken();
    
    if (tokenizer.hasMoreTokens()) {
        String type = tokenizer.nextToken();
        type = type.trim();
        
        if (type.equalsIgnoreCase("euc_2d"))
        weightType = 0;
        else
        if (type.equalsIgnoreCase("geo"))
            weightType = 1;
        else
            throw new TSPParseException("Diese Typ der Kantengewichte wird nicht unterstuetzt!");
    }
    else
        throw new TSPParseException("Diese Datei hat keinen Typ fuer die Kantengewichte!");      
    }
    
    protected void checkNodeType(String str) throws TSPParseException {
    StringTokenizer tokenizer = new StringTokenizer(str, ":");
    tokenizer.nextToken();
    
    if (tokenizer.hasMoreTokens()) {
        String type = tokenizer.nextToken();
        type = type.trim();
        
        if (!type.equalsIgnoreCase("twod_coords"))
        throw new TSPParseException("Dieser Knotentyp wird nicht unterstuetzt!");
    }
    }

    public void readVertices() throws TSPParseException {
    if (vertex == null)
        throw new TSPParseException("In dieser Datei ist keine Dimension angegeben!");

    try {
        for (int i=0; i<vertex.length; i++) {
        String str = quelle.readLine();
        StringTokenizer tokenizer = new StringTokenizer(str);
        while (!tokenizer.hasMoreTokens()) {
            str = quelle.readLine();
            tokenizer = new StringTokenizer(str);
        }
        
        tokenizer.nextToken();
        vertex[i][0] = Double.valueOf(tokenizer.nextToken()).doubleValue();
        vertex[i][1] = Double.valueOf(tokenizer.nextToken()).doubleValue();

        if (tokenizer.hasMoreTokens())
            throw new TSPParseException("Falsches Knoteneingabeformat");
        }
    }
    catch (IOException e) {
        throw new TSPParseException("Fehler beim Lesen der Knotenkoordinaten!: " + e.getMessage());
    }
    catch (NumberFormatException e) {
        throw new TSPParseException("Fehler beim Konvertieren der Knotenkoordinaten!!: " + e.getMessage());
    }
    catch (NoSuchElementException e) {
        throw new TSPParseException("Es fehlen Knotenkoordinaten!!: " + e.getMessage());
    }
    catch (NullPointerException e) {
        throw new TSPParseException("Es fehlen Knoten!!: " + e.getMessage());
    }
    }
    
    public void print()
    {
        int[][] tmp = toIntArray();
        for(int i=0;i<tmp.length;i++) 
            {
            for(int j=0;j<tmp[i].length;j++) 
            {
                System.out.print(tmp[i][j]+";");
            }
            System.out.println();
        }
    }
    
    /**
     * returns double matrix
     */
    public  double[][] getGraph(String name)
    {
         TSPLIB_Parser City1 = null;
         try
         {
            City1 = new TSPLIB_Parser("_tspFiles/EUC_2D/"+name);
         }
         catch(java.io.IOException bla)
         {
             System.out.println("Error: "+bla);
         }
         catch(TSPParseException message)
         {
            System.out.println(message);
         }
         return City1.toDoubleMatrix();
    }
    
  
    public  int[][] getIntGraph(String name)
    {
         TSPLIB_Parser City1 = null;
         try
         {
            City1 = new TSPLIB_Parser("_tspFiles/EUC_2D/"+name);
         }
         catch(java.io.IOException bla)
         {
             System.out.println("Error: "+bla);
         }
         catch(TSPParseException message)
         {
            System.out.println(message);
         }
         return City1.toIntMatrix();
    }

    public  void printGraph()
        {
            double[][] graph = toDoubleMatrix();
            String output= "x-> \t";
            for(int tmp =0; tmp<graph.length;tmp++)
            {
                //prints headline
                output +=align(tmp,6)+"\t";
            }
            output +="\n y  \t";
            for(int tmp=0; tmp<graph.length;tmp++)
            {
                output+=" ----- "+"\t";
            }
            output +="\n";    
            for(int y = 0; y<graph.length;y++)
            {
                output+= align(y,4) + " | "; 
                for(int x=0;x<graph[0].length;x++)
                {
                    if (graph[x][y] > 1e10)
                    {
                        //write infinit (-1)
                        output += align(-1,6)+"\t";
                    }
                    else 
                    {
                        output += align((int)(graph[x][y]),6)+"\t";
                    }
                }
                output += "\n";
            }
            System.out.println(output);
        }
        
         /**
      * Calculates a nice look like for printing the graph
      */
    private  String align(int number , int length) 
        {
            //get string
            String tmp = new String("" + number);
            // Berechnung der benötigten Leerstellen
            int length2add= length - tmp.length();
            for (int i=1; i <= length2add; i++) 
            {
                tmp= " " + tmp;
            }
            return tmp;
        }
}
